/// <reference path="jquery.d.ts"/>
/// <reference path="jqueryui.d.ts"/>

module SpreedSheet {
    export interface IStorage {
        isReady(): boolean;
        start();
        stop();
        save(value: string);
        load(): string;
    }

    export interface ISerialize {
        save(media: IStorage);
        load(media: IStorage);
    }


    export interface IAssumeData {
        assumeData(): void;
    }

    export interface IMakeDom {
        makeDom(): JQuery
    }
        
    export class StringStorage implements IStorage {
        count: number = 0;
        lines: string[] = [];
        isReadyFlag: boolean = false;

        constructor() {
            
        }
        public isReady(): boolean {
            return this.isReadyFlag;
        }
        public start() {
            this.count = 0;
//            this.lines = this.allText.split("|");
            this.isReadyFlag = true;
        }
        public stop() {
        }
        public save(value: string) {
            this.lines.push(value);
            this.count++;
        }
        public load(): string {
            this.count++;
            return this.lines[this.count - 1];
        }
    }
    export class DefaultStorage extends StringStorage
    {
        defaultText: string = "10|10|7|6||||Sample Bill|||||||||No.|Product|Units|Price|Net||||||1|Paper|12|1|={<}*{<<}||||||={^}+1|Pencil|3|4|={<}*{<<}||||||={^}+1|Pen|3|5|={<}*{<<}||||||={^}+1|Books|2|80|={<}*{<<}|||||||||Sum|={^}+{^^}+{^^^}+{^^^^}||||||||||||||||||||||||||||||||||||";
        
        public start() : void 
        {
            this.count = 0;
            this.lines = this.defaultText.split("|");
            this.isReadyFlag = true;
        }
    }


    export class WebStorage implements IStorage {
        count: number;
        savetext: string = "";

        constructor() {
            this.start();
        }
        public isReady(): boolean {
            return true;
        }
        public start() {
            this.count = 1;
        }
        public stop() {
        }
        public save(value: string) {
            window.localStorage.setItem(this.count.toString(), value);
            this.count++;
        }
        public load(): string {
            this.count++;
            return window.localStorage.getItem((this.count-1).toString());
        }
    }

    export class Cell implements IAssumeData,ISerialize,IMakeDom {
        public page    : string = "";
        public value   : number =  0;
        private formula : string = "";
       
        constructor(public currentRow: number,
            public currentColumn: number) {

        }
        public setFormula(str: string) {
            str = str.replace('\n', '');
            str = str.replace('\r', '');
            this.formula = str;
        }
        public getFormula() {
            return this.formula;
        }
        public save(media: IStorage) {
            media.save(this.formula);
        }

        public load(media: IStorage) {
            this.setFormula(media.load());
        }


        public assumeData() {
            this.formula = "";
            this.fromFormulaToValueToPage();
        }

        public fromFormulaToValueToPage() {
            if (this.formula.substring(0,1) == '=') {
                this.value = 0;
                this.page = this.formula.substring(1);
                if (atable) {
                    atable.evaluateFormula(this.currentRow, this.currentColumn);
                }
            }else if (this.formula.substring(0,1) == '\''){
                this.value = 0;
                this.page = this.formula.substring(1);
            }else{
                try {
                    this.value = parseFloat(this.formula);
                    if (isNaN(this.value)) {
                        this.value = 0;
                        this.page = this.formula;

                    } else {
                        this.page = this.value.toString();
                    }
                }catch(Exception){
                    this.value = 0;
                    this.page = this.formula;
                }
            }
            
        }
        public makeDom(): JQuery {
            return ($('<td></td>')
                .text(this.formula)
                .addClass('column-' + this.currentColumn)
                .addClass('row-' + this.currentRow)
                .data('column', this.currentColumn)
                .data('row', this.currentRow)
                );
        }

        public select(): JQuery {
            var classRow    = '.' + 'row-'    + this.currentRow;
            var classColumn = '.' + 'column-' + this.currentColumn;
            return $(classRow + classColumn);
        }

        public calcAll() {
            this.fromFormulaToValueToPage();
            this.select().text(this.page);
        }

    }

    export class Row implements IAssumeData,ISerialize,IMakeDom {
        public cells: Cell[] = [];

        constructor(public countColumn: number, public currentRow: number) {
 
        }
        public save(media: IStorage) {
            for (var i = 0; i < this.cells.length; i++) {
                this.cells[i].save(media);
            }
        }

        public load(media: IStorage) {
            
            this.cells = [];
            for (var i = 0; i < this.countColumn; i++) {
                var newCell = new Cell(this.currentRow, i);
                newCell.load(media);
                this.cells.push(newCell);
            }
        }

        public assumeData() {
            for (var i = 0; i < this.countColumn; i++) {
                var newCell = new Cell(this.currentRow,i);
                newCell.assumeData();
                this.cells.push(newCell);
            }
        }
        public calcAll() {
            for (var i = 0; i < this.cells.length; i++) {
                this.cells[i].calcAll();
            }
        }

        public makeDom(): JQuery {
            var jqRow = $('<tr></tr>');
            for (var i = 0; i < this.cells.length; i++) {
                jqRow.append(this.cells[i].makeDom());
            }
            return jqRow;
        }

    }

    export class Table implements IAssumeData,ISerialize,IMakeDom {
        currentRow     : number = 0;
        currentColumn: number = 0;
        countRow: number = 10;
        countColumn: number = 10;

        rows: Row[] = [];

        constructor() {
            this.assumeData();
            var test = window.localStorage.getItem("1");
            if (test != "10") {
                this.loadDefault();
            } else {
                this.load();
            }
        }

        storage: IStorage = new WebStorage();
        defaultStorage: IStorage = new DefaultStorage();

        public save(media: IStorage = this.storage) {
            media.start();
            while (!media.isReady()) {
            }
            media.save(this.countRow.toString());
            media.save(this.countColumn.toString());
            media.save(this.currentRow.toString());
            media.save(this.currentColumn.toString());
            for (var i = 0; i < this.rows.length; i++) {
                this.rows[i].save(media);
            }
            media.stop();
        }
        public loadDefault() {
            this.load(this.defaultStorage);
        }
        public load(media: IStorage = this.storage) {
            media.start();
            while (!media.isReady()) {
            }
            this.countRow = parseInt(media.load());
            this.countColumn = parseInt(media.load());
            this.currentRow = parseInt(media.load());
            this.currentColumn = parseInt(media.load());
            this.rows = [];
            for (var i = 0; i < this.countRow; i++) {
                var newRow = new Row(this.countColumn, i);
                newRow.load(media);
                this.rows.push(newRow);
            }
            media.stop();
            this.refreshPage();
        }

        assumeData() {
            for (var i = 0; i < this.countRow; i++) {
                var newRow = new Row(this.countColumn,i);
                newRow.assumeData();
                this.rows.push(newRow);
            }
        }
        public plugAllEvent() {
            this.plugOkCancel();
            this.plugKeyPress();
            this.plugSaveLoad();
        }
        public refreshPage() {
            this.clearTable();
            this.makeDom();
            this.focusCurrent();
            this.refreshClickable();

            this.calcAll();
            $('#ibar').focus();
        }
        public evaluateRelation(irow: number, icolumn: number,relation: string): string {
            var y: number = 0;
            var x: number = 0;
            for (var i = 0; i < relation.length; i++) {
                switch (relation.charAt(i)) {
                    case '<':
                        x--;
                        break;
                    case '>':
                        x++;
                        break;
                    case '^':
                        y--;
                        break;
                    case 'v':
                        y++;
                        break;
                }
            }
            var isRelative = (x != 0 || y != 0);
            if ( isRelative && irow + y >= 0 && icolumn + x >= 0) {
                return this.rows[irow+y].cells[icolumn+x].value.toString();
            }
            var word = relation.substring(1, relation.length - 1).toUpperCase();
            if (word == "SUMUP") {
                return this.sumUp(icolumn, irow).toString();
            } else if (word == "SUMLEFT") {
                return this.sumRight(icolumn, irow).toString();
            }
                
            return "0";
        }
        sumUp(column: number, row: number): number {
            var sum: number = 0.0;
            for (var irow = 0; irow < row; irow++) {
                sum = sum + (this.rows[irow].cells[column].value);
            }
            return sum;
        }
        sumRight(column: number, row: number): number {
            var sum: number = 0.0;
            for (var icol = 0; icol < column; icol++) {
                sum += (this.rows[row].cells[icol].value);
            }
            return sum;
        }
        public calcAll() {
            for (var irow = 0; irow < this.rows.length; irow++) {
                this.rows[irow].calcAll();
            } 
        }
        public evaluateFormula(irow: number, icolumn: number) {
            var formula = this.rows[irow].cells[icolumn].getFormula();
            if (formula.substring(0, 1) == '=') {
                formula = formula.substring(1);
                var regEx = new RegExp('{.*?}');
                try {
                    do {
                        var regExArray = regEx.exec(formula);
                        if (regExArray.length <= 0) {
                            break;
                        }
                        var relationValue = this.evaluateRelation(irow, icolumn, regExArray[0]);
                        formula = formula.replace(regExArray[0], relationValue);
                    } while (true);
                } catch(Exception) {

                }
            }
            var value = eval(formula);
            if (!isNaN(value)) {
                this.rows[irow].cells[icolumn].value = value;
                this.rows[irow].cells[icolumn].page = value.toString();
                var page = value.toString();
                this.rows[irow].cells[icolumn].select().text(page);
            }
        }
        plugKeyPress() {
            var that = this;
            $('body').keydown(function (e) {
                switch(e.keyCode)
                {
                    case 8://backspace
                    case 46://delete
                        $('#ibar').text('');
                        break;
                }
                switch (e.keyCode) {
                    case 8://backspace
                    case 46://delete
                    case 13:
                    case 37:
                    case 38:
                    case 39:
                    case 40:
                        that.rows[that.currentRow].cells[that.currentColumn].setFormula($('#ibar').text());
                        that.rows[that.currentRow].cells[that.currentColumn].calcAll();
                        that.calcAll();
                        break;
                }
                switch (e.keyCode) {
                    case 37: //left
                        that.unfocusCurrent();
                        that.currentColumn--;
                        if (that.currentColumn < 0) {
                            that.currentColumn = 0;
                        }
                        that.focusCurrent();
                        
                        break;
                    case 38: //up
                        that.unfocusCurrent();
                        that.currentRow--;
                        if (that.currentRow < 0) {
                            that.currentRow = 0;
                        }
                        that.focusCurrent();
                        
                        break;
                    case 39: //right
                        that.unfocusCurrent();
                        that.currentColumn++;
                        if (that.currentColumn >= that.countColumn) {
                            that.currentColumn = that.countColumn-1;
                        }
                        that.focusCurrent();
                        
                        break;
                    case 13:
                    case 40: //bottom
                        that.unfocusCurrent();
                        that.currentRow++;
                        if (that.currentRow >= that.countRow) {
                            that.currentRow = that.countRow-1;
                        }
                        that.focusCurrent();
                        
                        break;
                }

                e.isDefaultPrevented = () => {
                    switch (e.keyCode) {
                        case 8:
                        case 46:
                        case 13:
                        case 37:
                        case 38:
                        case 39:
                        case 40:
                            return true;
                            break;
                        default:
                            break;
                    }
                    return false;
                };   
                
            });
        }
        plugSaveLoad() {
            var that = this;
            $('#isave').click(function () {
                that.save();
            });
            $('#iload').click(function () {
                that.load();
            });
        }
        plugOkCancel() {
            var that = this;
            $('#iok').click(function () {
                that.rows[that.currentRow].cells[that.currentColumn].setFormula($('#ibar').text());
                that.rows[that.currentRow].cells[that.currentColumn].calcAll();
                that.calcAll();
            });
            $('#icancel').click(function () {
                $('#ibar').text(that.rows[that.currentRow].cells[that.currentColumn].getFormula());
            });
        }
        unfocusCurrent() {
            $('tr').eq(this.currentRow).children('td').eq(this.currentColumn).css('background-color', 'white');
        }
        focusCurrent() {
            $('tr').eq(this.currentRow).children('td').eq(this.currentColumn).css('background-color', 'yellow');
            this.showOnBar();
        }

        showOnBar() {
            $('#ibar').text( this.rows[this.currentRow].cells[this.currentColumn].getFormula());
        }

        refreshClickable() {
            var that = this;
            $('td').unbind('click');
            $('td').click(function (e) {
                that.unfocusCurrent();
                that.currentRow = $(this).data('row');
                that.currentColumn = $(this).data('column');
                that.focusCurrent();
            });
        }

        clearTable() {
            $('#itable').children().remove();
        }

        makeDom(): JQuery {
            var that = this;

            var jqRow = $('#itable');
            for (var i = 0; i < this.rows.length; i++) {
                jqRow.append(that.rows[i].makeDom());
            }
            return jqRow;
        }
    }
    var passed = false;
    var atable = new Table();

    $(document).ready(() => {
        atable.plugAllEvent();
        atable.refreshPage();
        $('#islot').click(()=>{$('#dialogSlot').dialog()});
    });
}
